'use strict';

var utils = require('../utils');

/**
 * 专门用于合并配置生成一个新配置对象的合并函数
 *
 * @param {Object} config1 配置1
 * @param {Object} config2 配置2
 * @returns {Object} 将config2与config1合并后产生的新对象
 */
module.exports = function mergeConfig(config1, config2) {
  // eslint-disable-next-line no-param-reassign
  config2 = config2 || {};
  var config = {};

  /**
   * 合并函数，后面的覆盖前面的
   *
   * `utils.merge`是会一定后面覆盖前面的，不会判断undefined，并且注意`utils.merge`只能合并对象
   *
   * @param {*} target 目标对象
   * @param {*} source 源对象
   * @returns 生成一个源对象覆盖目标对象后的新对象
   */
  function getMergedValue(target, source) {
    if (utils.isPlainObject(target) && utils.isPlainObject(source)) {
      return utils.merge(target, source);
    } else if (utils.isPlainObject(source)) {
      return utils.merge({}, source);
    } else if (utils.isArray(source)) {
      return source.slice();
    }
    return source;
  }

  /**
   * 合并深层属性
   * @param {*} prop 属性
   * @returns 和原来没关系的新属性
   */
  // eslint-disable-next-line consistent-return
  function mergeDeepProperties(prop) {
    if (!utils.isUndefined(config2[prop])) {
      return getMergedValue(config1[prop], config2[prop]);
    } else if (!utils.isUndefined(config1[prop])) {
      return getMergedValue(undefined, config1[prop]);
    }
  }

  /**
   * 从config2中取值
   * 如果config2中获取不到值就返回undefined
   * @param {*} prop 属性
   * @returns 值（和原值没有关系）
   */
  // eslint-disable-next-line consistent-return
  function valueFromConfig2(prop) {
    if (!utils.isUndefined(config2[prop])) {
      return getMergedValue(undefined, config2[prop]);
    }
  }

  /**
   * 从config1、config2中获取一个属性值，config2优先
   * @param {*} prop 属性
   * @returns 值（和原值没有关系）
   */
  // eslint-disable-next-line consistent-return
  function defaultToConfig2(prop) {
    if (!utils.isUndefined(config2[prop])) {
      return getMergedValue(undefined, config2[prop]);
    } else if (!utils.isUndefined(config1[prop])) {
      return getMergedValue(undefined, config1[prop]);
    }
  }

  /**
   * 从config1、config2中获取一个属性值，config2优先
   * @param {*} prop 属性
   * @returns 值（和原值没有关系）
   */
  // eslint-disable-next-line consistent-return
  function mergeDirectKeys(prop) {
    if (prop in config2) {
      return getMergedValue(config1[prop], config2[prop]);
    } else if (prop in config1) {
      return getMergedValue(undefined, config1[prop]);
    }
  }

  // 对象key的合并策略
  var mergeMap = {
    url: valueFromConfig2,
    method: valueFromConfig2,
    data: valueFromConfig2,
    baseURL: defaultToConfig2,
    transformRequest: defaultToConfig2,
    transformResponse: defaultToConfig2,
    paramsSerializer: defaultToConfig2,
    timeout: defaultToConfig2,
    timeoutMessage: defaultToConfig2,
    withCredentials: defaultToConfig2,
    adapter: defaultToConfig2,
    responseType: defaultToConfig2,
    xsrfCookieName: defaultToConfig2,
    xsrfHeaderName: defaultToConfig2,
    onUploadProgress: defaultToConfig2,
    onDownloadProgress: defaultToConfig2,
    decompress: defaultToConfig2,
    maxContentLength: defaultToConfig2,
    maxBodyLength: defaultToConfig2,
    transport: defaultToConfig2,
    httpAgent: defaultToConfig2,
    httpsAgent: defaultToConfig2,
    cancelToken: defaultToConfig2,
    socketPath: defaultToConfig2,
    responseEncoding: defaultToConfig2,
    validateStatus: mergeDirectKeys,
  };

  utils.forEach(
    Object.keys(config1).concat(Object.keys(config2)),
    function computeConfigValue(prop) {
      // 合并方法
      var merge = mergeMap[prop] || mergeDeepProperties;
      var configValue = merge(prop);
      (utils.isUndefined(configValue) && merge !== mergeDirectKeys) ||
        // 如果是从合并方法中取出来的undefined，那么就说明两个config都不存在这个值，那么直接不要，即不执行下面代码
        (config[prop] = configValue);
    }
  );

  return config;
};
